package com.hsjk.szwj.module.pay.service;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingFinishRequest;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingQueryRequest;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingQueryResult;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingReceiverRequest;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingReceiverResult;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingRequest;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingResult;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingReceiver;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingUnfreezeRequest;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingUnfreezeResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.hsjk.szwj.framework.common.exception.ServiceException;
import com.hsjk.szwj.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.hsjk.szwj.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.hsjk.szwj.module.pay.controller.admin.division.vo.AdminDivisionAccountTypeReqVO;
import com.hsjk.szwj.module.pay.controller.admin.division.vo.AdminDivisionAccountTypeRespVO;
import com.hsjk.szwj.module.pay.convert.DivisionConvert;
import com.hsjk.szwj.module.pay.dal.dataobject.config.ChannelRetMsg;
import com.hsjk.szwj.module.pay.dal.dataobject.config.MchAppConfigContext;
import com.hsjk.szwj.module.pay.dal.dataobject.config.WxServiceWrapper;
import com.hsjk.szwj.module.pay.dal.dataobject.config.WxpayIsvsubMchParams;
import com.hsjk.szwj.module.pay.dal.dataobject.division.MchDivisionReceiverDO;
import com.hsjk.szwj.module.pay.dal.dataobject.order.PayOrderDO;
import com.hsjk.szwj.module.pay.dal.dataobject.order.PayOrderDivisionRecordDO;
import com.hsjk.szwj.module.pay.dal.mysql.IMchDivisionReceiverMapper;
import com.hsjk.szwj.module.pay.enums.IfCodeEnum;
import com.hsjk.szwj.module.pay.enums.WxPayVersionEnum;
import com.hsjk.szwj.module.pay.util.SeqKit;
import com.hsjk.szwj.module.pay.util.WxpayKit;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author chenjiawei
 * @desc 分账接口： 微信官方
 * @Date 2023/08/25 09:22
 **/
@Slf4j
@Service
public class WxpayDivisionService implements IDivisionService {

    @Resource
    private ConfigContextQueryService configContextQueryService;

    @Resource
    private PayInterfaceDefineService payInterfaceDefineService;

    @Resource
    private IMchDivisionReceiverMapper mchDivisionReceiverMapper;

    @Override
    public String getIfCode() {
        return IfCodeEnum.WXPAY.getCode();
    }

    @Override
    public boolean isSupport() {
        return false;
    }

    @Override
    public ChannelRetMsg bind(MchDivisionReceiverDO mchDivisionReceiver, MchAppConfigContext mchAppConfigContext) {
        try {

            WxServiceWrapper wxServiceWrapper =
                    configContextQueryService.getWxServiceWrapper(mchAppConfigContext);
            Long ifCodeId = payInterfaceDefineService.getIdByCode(getIfCode());
            if (WxPayVersionEnum.WX_V2.getVersion().equals(wxServiceWrapper.getApiVersion())) {
                //V2
                ProfitSharingReceiverRequest request = new ProfitSharingReceiverRequest();

                //设置ifcodeid
                mchAppConfigContext.setIfCodeId(ifCodeId);
                //放置isv信息
                WxpayKit.putApiIsvInfo(mchAppConfigContext, request);

                JSONObject receiverJSON = new JSONObject();

                // 0-个人， 1-商户  (目前仅支持服务商appI获取个人openId, 即： PERSONAL_OPENID， 不支持 PERSONAL_SUB_OPENID )
                receiverJSON.putIfAbsent("type", mchDivisionReceiver.getAccType() == 0 ? "PERSONAL_OPENID" : "MERCHANT_ID");
                receiverJSON.putIfAbsent("account", mchDivisionReceiver.getAccNo());
                receiverJSON.putIfAbsent("name", mchDivisionReceiver.getAccName());
                receiverJSON.putIfAbsent("relation_type", mchDivisionReceiver.getRelationType());
                receiverJSON.putIfAbsent("custom_relation", mchDivisionReceiver.getRelationTypeName());
                request.setReceiver(receiverJSON.toString());

                ProfitSharingReceiverResult profitSharingReceiverResult =
                        wxServiceWrapper.getWxPayService().getProfitSharingService().addReceiver(request);

                // 明确成功
                return ChannelRetMsg.confirmSuccess(null);
            } else if (WxPayVersionEnum.WX_V3.getVersion().equals(wxServiceWrapper.getApiVersion())) {

                ProfitSharingReceiver profitSharingReceiver = new ProfitSharingReceiver();
                profitSharingReceiver.setType(mchDivisionReceiver.getAccType() == 0 ? "PERSONAL_OPENID" : "MERCHANT_ID");
                profitSharingReceiver.setAccount(mchDivisionReceiver.getAccNo());
                profitSharingReceiver.setName(mchDivisionReceiver.getAccName());
                profitSharingReceiver.setRelationType(mchDivisionReceiver.getRelationType());
                profitSharingReceiver.setCustomRelation(mchDivisionReceiver.getRelationTypeName());

                profitSharingReceiver.setAppid(WxpayKit.getWxPayConfig(wxServiceWrapper).getAppId());
                // 特约商户
                if (mchAppConfigContext.isIsvsubMch()) {
                    WxpayIsvsubMchParams isvsubMchParams =
                            (WxpayIsvsubMchParams) configContextQueryService
                                    .queryIsvsubMchParams(mchAppConfigContext.getMchId(),
                                            mchAppConfigContext.getAppId(), ifCodeId);

                    profitSharingReceiver.setSubMchId(String.valueOf(isvsubMchParams.getSubMchId()));
                }

                ProfitSharingReceiver receiver = wxServiceWrapper.getWxPayService().getProfitSharingV3Service().addProfitSharingReceiver(profitSharingReceiver);

                // 明确成功
                return ChannelRetMsg.confirmSuccess(null);
            } else {
                return ChannelRetMsg.sysError("请选择微信V2或V3模式");
            }

        } catch (WxPayException wxPayException) {
            ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
            WxpayKit.commonSetErrInfo(channelRetMsg, wxPayException);
            return channelRetMsg;

        } catch (Exception e) {

            log.error("请求微信绑定分账接口异常！", e);
            ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
            channelRetMsg.setChannelErrMsg("系统异常：" + e.getMessage());
            return channelRetMsg;
        }
    }

    @Override
    public ChannelRetMsg singleDivision(PayOrderDO payOrder, List<PayOrderDivisionRecordDO> recordList,
                                        MchAppConfigContext mchAppConfigContext) {
        log.info("singleDivision-payOrder:{},recordList:{}", payOrder, recordList);
        try {
            WxServiceWrapper wxServiceWrapper = configContextQueryService.getWxServiceWrapper(mchAppConfigContext);
            Long ifCodeId = payInterfaceDefineService.getIdByCode(getIfCode());
            if (WxPayVersionEnum.WX_V2.getVersion().equals(wxServiceWrapper.getApiVersion())) {
                //V2

                ProfitSharingRequest request = new ProfitSharingRequest();
                request.setTransactionId(payOrder.getChannelOrderNo());
                //设置ifcodeid
                mchAppConfigContext.setIfCodeId(ifCodeId);
                //放置isv信息
                WxpayKit.putApiIsvInfo(mchAppConfigContext, request);

                if (recordList.isEmpty()) {
                    // 随机生成一个订单号
                    request.setOutOrderNo(SeqKit.genDivisionBatchId());
                } else {
                    //取到批次号
                    request.setOutOrderNo(String.valueOf(recordList.get(0).getBatchOrderId()));
                }

                JSONArray receiverJSONArray = new JSONArray();

                for (int i = 0; i < recordList.size(); i++) {

                    PayOrderDivisionRecordDO record = recordList.get(i);
                    if (record.getCalDivisionAmount() <= 0) {
                        continue;
                    }

                    JSONObject receiverJSON = new JSONObject();
                    // 0-个人， 1-商户  (目前仅支持服务商appI获取个人openId, 即： PERSONAL_OPENID， 不支持 PERSONAL_SUB_OPENID )
                    receiverJSON.put("type", record.getAccType() == 0 ? "PERSONAL_OPENID" : "MERCHANT_ID");
                    receiverJSON.put("account", record.getAccNo());
                    receiverJSON.put("amount", record.getCalDivisionAmount());
                    receiverJSON.put("description", record.getPayOrderId() + "分账");
                    receiverJSONArray.add(receiverJSON);
                }

                //不存在接收账号时，订单完结（解除冻结金额）
                if (receiverJSONArray.isEmpty()) {
                    return ChannelRetMsg.confirmSuccess(this.divisionFinish(payOrder, mchAppConfigContext));
                }

                request.setReceivers(receiverJSONArray.toString());

                ProfitSharingResult profitSharingResult = wxServiceWrapper.getWxPayService().getProfitSharingService().profitSharing(request);
                return ChannelRetMsg.waiting();

            } else if (WxPayVersionEnum.WX_V3.getVersion().equals(wxServiceWrapper.getApiVersion())) {

                com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingRequest request = new com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingRequest();
                request.setTransactionId(payOrder.getChannelOrderNo());
                request.setUnfreezeUnsplit(true);

                request.setAppid(WxpayKit.getWxPayConfig(wxServiceWrapper).getAppId());
                // 特约商户
                if (mchAppConfigContext.isIsvsubMch()) {
                    WxpayIsvsubMchParams isvsubMchParams =
                            (WxpayIsvsubMchParams) configContextQueryService
                                    .queryIsvsubMchParams(mchAppConfigContext.getMchId(),
                                            mchAppConfigContext.getAppId(), ifCodeId);

                    request.setSubMchId(String.valueOf(isvsubMchParams.getSubMchId()));
                }

                if (recordList.isEmpty()) {
                    // 随机生成一个订单号
                    request.setOutOrderNo(SeqKit.genDivisionBatchId());
                } else {
                    //取到批次号
                    request.setOutOrderNo(String.valueOf(recordList.get(0).getBatchOrderId()));
                }

                List<ProfitSharingReceiver> receivers = new ArrayList<>();
                for (int i = 0; i < recordList.size(); i++) {

                    PayOrderDivisionRecordDO record = recordList.get(i);
                    if (record.getCalDivisionAmount() <= 0) {
                        continue;
                    }

                    ProfitSharingReceiver receiver = new ProfitSharingReceiver();
                    // 0-个人， 1-商户  (目前仅支持服务商appI获取个人openId, 即： PERSONAL_OPENID， 不支持 PERSONAL_SUB_OPENID )
                    receiver.setType(record.getAccType() == 0 ? "PERSONAL_OPENID" : "MERCHANT_ID");
                    receiver.setAccount(record.getAccNo());
                    receiver.setAmount(record.getCalDivisionAmount());
                    receiver.setDescription(record.getPayOrderId() + "分账");
                    receivers.add(receiver);
                }
                //不存在接收账号时，订单完结（解除冻结金额）
                if (receivers.isEmpty()) {
                    return ChannelRetMsg.confirmSuccess(this.divisionFinish(payOrder, mchAppConfigContext));
                }
                request.setReceivers(receivers);

                com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingResult profitSharingResult = wxServiceWrapper.getWxPayService().getProfitSharingV3Service().profitSharing(request);

                return ChannelRetMsg.waiting();

            } else {

                return ChannelRetMsg.sysError("请选择微信V2或V3模式");

            }

        } catch (WxPayException wxPayException) {

            ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
            WxpayKit.commonSetErrInfo(channelRetMsg, wxPayException);
            return channelRetMsg;

        } catch (Exception e) {
            log.error("微信分账失败", e);
            ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
            channelRetMsg.setChannelErrMsg(e.getMessage());
            return channelRetMsg;
        }
    }

    @Override
    public HashMap<Long, ChannelRetMsg> queryDivision(PayOrderDO payOrder, List<PayOrderDivisionRecordDO> recordList, MchAppConfigContext mchAppConfigContext) {

        // 创建返回结果
        HashMap<Long, ChannelRetMsg> resultMap = new HashMap<>();
        try {

            WxServiceWrapper wxServiceWrapper = configContextQueryService.getWxServiceWrapper(mchAppConfigContext);
            Long ifCodeId = payInterfaceDefineService.getIdByCode(getIfCode());

            // 得到所有的 accNo 与 recordId map
            Map<String, Long> accnoAndRecordIdSet = new HashMap<>();
            for (PayOrderDivisionRecordDO record : recordList) {
                accnoAndRecordIdSet.put(record.getAccNo(), record.getId());
            }

            if (WxPayVersionEnum.WX_V2.getVersion().equals(wxServiceWrapper.getApiVersion())) {
                //V2

                ProfitSharingQueryRequest request = new ProfitSharingQueryRequest();
                request.setTransactionId(payOrder.getChannelOrderNo());
                //设置ifcodeid
                mchAppConfigContext.setIfCodeId(ifCodeId);
                //放置isv信息
                WxpayKit.putApiIsvInfo(mchAppConfigContext, request);
                //取到批次号
                request.setOutOrderNo(String.valueOf(recordList.get(0).getBatchOrderId()));

                ProfitSharingQueryResult profitSharingQueryResult = wxServiceWrapper.getWxPayService().getProfitSharingService().profitSharingQuery(request);
                List<ProfitSharingQueryResult.Receiver> receivers = profitSharingQueryResult.getReceivers();

                for (ProfitSharingQueryResult.Receiver receiver : receivers) {

                    // 我方系统的分账接收记录ID
                    Long recordId = accnoAndRecordIdSet.get(receiver.getAccount());

                    // 记录中包含账号
                    if (recordId != null) {

                        // 仅返回分账记录为最终态的结果 处理中的分账单不做返回处理
                        if ("SUCCESS".equals(receiver.getResult())) {

                            resultMap.put(recordId, ChannelRetMsg.confirmSuccess(null));

                        } else if ("CLOSED".equals(receiver.getResult())) {

                            resultMap.put(recordId, ChannelRetMsg.confirmFail(null, null, receiver.getFailReason()));
                        }
                    }

                }

            } else if (WxPayVersionEnum.WX_V3.getVersion()
                    .equals(wxServiceWrapper.getApiVersion())) {

                String url = String.format("%s/v3/profitsharing/orders/%s?transaction_id=%s",
                        wxServiceWrapper.getWxPayService().getPayBaseUrl(), recordList.get(0).getBatchOrderId(), payOrder.getChannelOrderNo());

                // 特约商户
                if (mchAppConfigContext.isIsvsubMch()) {
                    WxpayIsvsubMchParams isvsubMchParams =
                            (WxpayIsvsubMchParams) configContextQueryService
                                    .queryIsvsubMchParams(mchAppConfigContext.getMchId(),
                                            mchAppConfigContext.getAppId(), ifCodeId);
                    url += "&sub_mchid=" + isvsubMchParams.getSubMchId();
                }

                String result = wxServiceWrapper.getWxPayService().getV3(url);

                com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingResult profitSharingResult
                        = JSONUtil.toBean(result, com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingResult.class);

                List<com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingResult.Receiver> receivers = profitSharingResult.getReceivers();

                for (com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingResult.Receiver receiver : receivers) {

                    // 我方系统的分账接收记录ID
                    Long recordId = accnoAndRecordIdSet.get(receiver.getAccount());

                    // 记录中包含账号
                    if (recordId != null) {

                        // 仅返回分账记录为最终态的结果 处理中的分账单不做返回处理
                        if ("SUCCESS".equals(receiver.getResult())) {

                            resultMap.put(recordId, ChannelRetMsg.confirmSuccess(null));

                        } else if ("CLOSED".equals(receiver.getResult())) {

                            resultMap.put(recordId, ChannelRetMsg.confirmFail(null, null, receiver.getFailReason()));
                        }
                    }

                }

            }

        } catch (WxPayException wxPayException) {
            log.error("微信查询分账结果失败, e = {}", wxPayException);
            throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                    wxPayException.getCustomErrorMsg());

        } catch (Exception e) {
            log.error("微信分账失败", e);
            throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                    e.getMessage());
        }

        return resultMap;
    }

    @Override
    public List<AdminDivisionAccountTypeRespVO> getAdminDivisionAccountInfo(AdminDivisionAccountTypeReqVO reqVO) {
        List<MchDivisionReceiverDO> mchDivisionReceiverDOS = mchDivisionReceiverMapper.selectList(new LambdaQueryWrapperX<MchDivisionReceiverDO>()
                .eqIfPresent(MchDivisionReceiverDO::getAccType, reqVO.getAccType()));
        return DivisionConvert.INSTANCE.convertAdminDivisionAccountTypeRespVO(mchDivisionReceiverDOS);
    }

    /**
     * 调用订单的完结接口 (分账对象不存在时)
     */
    private String divisionFinish(PayOrderDO payOrder,
                                  MchAppConfigContext mchAppConfigContext) throws WxPayException {

        WxServiceWrapper wxServiceWrapper = configContextQueryService.getWxServiceWrapper(mchAppConfigContext);
        Long ifCodeId = payInterfaceDefineService.getIdByCode(getIfCode());
        if (WxPayVersionEnum.WX_V2.getVersion().equals(wxServiceWrapper.getApiVersion())) {
            //V2

            ProfitSharingFinishRequest request = new ProfitSharingFinishRequest();
            //设置ifcodeid
            mchAppConfigContext.setIfCodeId(ifCodeId);
            //放置isv信息
            WxpayKit.putApiIsvInfo(mchAppConfigContext, request);
            // 传入subAppId 将导致签名失败
            request.setSubAppId(null);

            request.setTransactionId(payOrder.getChannelOrderNo());
            request.setOutOrderNo(SeqKit.genDivisionBatchId());
            request.setDescription("完结分账");

            return wxServiceWrapper.getWxPayService()
                    .getProfitSharingService().profitSharingFinish(request).getOrderId();

        } else {

            ProfitSharingUnfreezeRequest request = new ProfitSharingUnfreezeRequest();
            // 特约商户
            if (mchAppConfigContext.isIsvsubMch()) {
                WxpayIsvsubMchParams isvsubMchParams =
                        (WxpayIsvsubMchParams) configContextQueryService
                                .queryIsvsubMchParams(mchAppConfigContext.getMchId(),
                                        mchAppConfigContext.getAppId(), ifCodeId);

                request.setSubMchId(String.valueOf(isvsubMchParams.getSubMchId()));
            }

            request.setTransactionId(payOrder.getChannelOrderNo());
            request.setOutOrderNo(SeqKit.genDivisionBatchId());
            request.setSubMchId(null);
            request.setDescription("完结分账");
            ProfitSharingUnfreezeResult profitSharingUnfreezeResult = wxServiceWrapper.getWxPayService().getProfitSharingV3Service().profitSharingUnfreeze(request);

            // 明确成功
            return profitSharingUnfreezeResult.getOrderId();
        }
    }
}
